﻿using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using ZSoftInstagram.DataAccess.Abstraction;
using ZSoftInstagram.DataAccess.Abstractions;
using EntityFramework.Extensions;

namespace ZSoftInstagram.DataAccess
{
    public class SqlRepository<T> : IRepository<T> where T : class, IEntity
    {
        private DbSet<T> _set;
        private ApplicationDbContext _context;
        public SqlRepository(ApplicationDbContext context)
        {
            _set = context.Set<T>();
            this._context = context;
        }

        public IRepository<TEntity> GetTypeRepository<TEntity>() where TEntity:class, IEntity
        {
            return new SqlRepository<TEntity>(_context);
        }

        public DbSet<T> UnderlyingSet
        {
            get
            {
                return _set;
            }
            
        }

        public IQueryable<T> All()
        {
            return _set;
        }

        public int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updateExpression)
        {
            return this._set.Where(predicate).Update(updateExpression);
        }

        public Task<int> UpdateAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updateExpression)
        {
            return this._set.Where(predicate).UpdateAsync(updateExpression);
        }

        public T First(params string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query = query.Include(inc));
            }

            return query.First();
        }
        

        public Task<T> FirstAsync(string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query = query.Include(inc));
            }
            return query.FirstAsync();
        }

        public T First(Func<T, bool> predicate, string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query = query.Include(inc));
            }
            return query.First(predicate);
        }

        public T FirstOrDefault(Func<T, bool> predicate, string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query = query.Include(inc));
            }
            return query.FirstOrDefault(predicate);
        }

        public Task<T> FirstAsync(Expression<Func<T, bool>> predicate, params string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query = query.Include(inc));
            }
            return query.FirstAsync(predicate);
        }

        public IQueryable<T> Where(Expression<Func<T, bool>> predicate, string[] includes)
        {
            IQueryable<T> query = _set;
            if (includes != null)
            {
                includes.ForEach(inc => query.Include(inc));
            }

            return query.Where(predicate).AsQueryable();
        }

        public bool Any(Func<T, bool> predicate)
        {
            return _set.Any(predicate);
        }

        public Task<bool> AnyAsync(Expression<Func<T, bool>> predicate)
        {
            return _set.AnyAsync(predicate);
        }

        public IRepository<T> Add(T item)
        {
            _set.Add(item);
            return this;
        }

        public IRepository<T> Remove(T item)
        {
            _set.Remove(item);
            return this;
        }

        public IRepository<T> Update(T item)
        {
            _context.Entry(item).State = EntityState.Modified;
            return this;
        }

        public IRepository<T> Attach(T item)
        {
            T old = _set.Local.FirstOrDefault(i => i.Id == item.Id);
            _set.Attach(item);
            return this;
        }

        public Task<int> JustDeleteAsync(Expression<Func<T, bool>> predicate)
        {
            return this._set.Where(predicate).DeleteAsync();
        }

        public Task<int> SaveChangesAsync()
        {
            return this._context.SaveChangesAsync();
        }

        public int SaveChanges()
        {
            return this._context.SaveChanges();
        }
    }
}
